package com.example.lotterysystem.service.impl;

import ch.qos.logback.core.util.StringUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.example.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import com.example.lotterysystem.common.exception.ServiceException;
import com.example.lotterysystem.common.utils.JacksonUtil;
import com.example.lotterysystem.common.utils.JwtUtil;
import com.example.lotterysystem.common.utils.RegexUtil;
import com.example.lotterysystem.controller.param.ShortMessageLoginParam;
import com.example.lotterysystem.controller.param.UserLoginParam;
import com.example.lotterysystem.controller.param.UserPasswordLoginParam;
import com.example.lotterysystem.controller.param.UserRegisterParam;
import com.example.lotterysystem.mapper.UserMapper;
import com.example.lotterysystem.pojo.dataobject.Encrypt;
import com.example.lotterysystem.pojo.dataobject.UserDO;
import com.example.lotterysystem.service.UserService;
import com.example.lotterysystem.service.VerificationCodeService;
import com.example.lotterysystem.service.dto.UserDTO;
import com.example.lotterysystem.service.dto.UserLoginDTO;
import com.example.lotterysystem.service.dto.UserRegisterDTO;
import com.example.lotterysystem.service.enums.UserIdentityEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private VerificationCodeService verificationCodeService;

    /**
     * 用户注册
     * @param param
     * @return
     */
    @Override
    public UserRegisterDTO register(UserRegisterParam param) {
        // 校验注册信息
        checkRegisterInfo(param);

        // 校验之后，进行处理: 加密私密数据（构造dao层请求）
        UserDO userDO = new UserDO();
        userDO.setUserName(param.getName());
        userDO.setEmail(param.getMail());
        userDO.setIdentity(param.getIdentity());
        // 手机号需要使用 Encrypt
        userDO.setPhoneNumber(new Encrypt(param.getPhoneNumber()));
        // 密码需要使用 HASH 加密的 sha256 方式进行加密
        if (StringUtils.hasText(param.getPassword())) {
            userDO.setPassword(DigestUtil.sha256Hex(param.getPassword()));
        }

        // 保存数据到 数据库中
        userMapper.insertUser(userDO);

        // 构造返回
        UserRegisterDTO userRegisterDTO = new UserRegisterDTO();
        userRegisterDTO.setUserId(userDO.getId());
        return userRegisterDTO;
    }

    /**
     * 用户登录
     * @param param
     * @return
     */
    @Override
    public UserLoginDTO login(UserLoginParam param) {
        UserLoginDTO userLoginDTO;

        // 类型的检查并且进行类型的转换 这种方法在 Java14以上就可以使用了
        if (param instanceof UserPasswordLoginParam loginParam) {
            // 密码登录
            userLoginDTO = loginByUserPassword(loginParam);
        }else if (param instanceof ShortMessageLoginParam loginParam) {
            // 验证码登录
            userLoginDTO = loginByShortMessage(loginParam);
        }else {
            throw new ServiceException(ServiceErrorCodeConstants.LOGIN_INFO_NOT_EXIST);
        }

        return userLoginDTO;
    }

    /**
     * 获取人员列表
     * @param identity
     * @return
     */
    @Override
    public List<UserDTO> findUserBaseInfo(UserIdentityEnum identity) {
        // 数据库中的 人员身份是 String类型的，所以需要进行转换
        String identityString = identity != null ? identity.name() : null;
        // 在数据库中进行查表
        // 先在数据库中获取库中的值 为 UserDO
        List<UserDO> userDOList = userMapper.selectUserListByIdentity(identityString);
        // 再将获取到的UserDO 转换成 UserDTO 进行返回
        List<UserDTO> userDTOList = userDOList.stream()
                .map(userDO -> {
                    UserDTO userDTO = new UserDTO();
                    userDTO.setUserId(userDO.getId());
                    userDTO.setUserName(userDO.getUserName());
                    userDTO.setEmail(userDO.getEmail());
                    userDTO.setPhoneNumber(userDO.getPhoneNumber().getValue());
                    userDTO.setIdentity(UserIdentityEnum.forName(userDO.getIdentity()));
                    return userDTO;
                }).collect(Collectors.toList());

        return userDTOList;
    }

    /**
     * 验证码登录
     * @param loginParam
     * @return
     */
    private UserLoginDTO loginByShortMessage(ShortMessageLoginParam loginParam) {
        if (!RegexUtil.checkMail(loginParam.getLoginEmail())) {
            throw new ServiceException(ServiceErrorCodeConstants.MAIL_ERROR);
        }

        UserDO userDO = userMapper.selectByEmail(loginParam.getLoginEmail());

        if (userDO == null) {
            throw new ServiceException(ServiceErrorCodeConstants.USER_INFO_IS_EMPTY);
        }else if (StringUtils.hasText(loginParam.getMandatoryIdentity())
                && !loginParam.getMandatoryIdentity().equalsIgnoreCase(userDO.getIdentity())) {
            // 强制登录，登录身份信息不一致
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);
        }
        // 校验验证码
        String code = verificationCodeService.getVerificationCode(loginParam.getLoginEmail());
//        System.out.println(code);
//        System.out.println(loginParam.getLoginEmail());
        if (!loginParam.getVerificationCode().equals(code)) {
            throw new ServiceException(ServiceErrorCodeConstants.VERIFICATION_CODE_ERROR);
        }

        // 走到这里说明正确，创建 JWT 返回
        Map<String,Object> claim = new HashMap<>();
        claim.put("userId",userDO.getId());
        claim.put("identity",userDO.getIdentity());
        String token = JwtUtil.getJwt(claim);

        UserLoginDTO userLoginDTO = new UserLoginDTO();
        userLoginDTO.setToken(token);
        userLoginDTO.setIdentity(UserIdentityEnum.forName(userDO.getIdentity()));
        return userLoginDTO;
    }

    /**
     * 密码登录
     * @param loginParam
     * @return
     */
    private UserLoginDTO loginByUserPassword(UserPasswordLoginParam loginParam) {
        UserDO userDO;
        // 判断手机登录还是邮箱登录
        if (RegexUtil.checkMail(loginParam.getLoginName())) {
            // 邮箱登录，就根据邮箱进行查询信息
            userDO = userMapper.selectByEmail(loginParam.getLoginName());
        }else if (RegexUtil.checkMobile(loginParam.getLoginName())) {
            // 手机号登录，就根据手机号进行查询信息
            // 手机号需要进行 加密
            userDO = userMapper.selectByPhoneNumber(new Encrypt(loginParam.getLoginName()));
        }else {
            throw new ServiceException(ServiceErrorCodeConstants.LOGIN_NOT_EXIST);
        }

        // 校验登录信息
        if (userDO == null) {
            throw new ServiceException(ServiceErrorCodeConstants.USER_INFO_IS_EMPTY);
        }else if (StringUtils.hasText(loginParam.getMandatoryIdentity())
                    && !loginParam.getMandatoryIdentity().equalsIgnoreCase(userDO.getIdentity())) {
            // 强制登录，登录身份信息不一致
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);
        }else if (!DigestUtil.sha256Hex(loginParam.getPassword()).equals(userDO.getPassword())){
            // 密码不一致
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_ERROR);
        }

        // 走到这里说明正确，创建 JWT 返回
        Map<String,Object> claim = new HashMap<>();
        claim.put("userId",userDO.getId());
        claim.put("identity",userDO.getIdentity());
        String token = JwtUtil.getJwt(claim);

        UserLoginDTO userLoginDTO = new UserLoginDTO();
        userLoginDTO.setToken(token);
        userLoginDTO.setIdentity(UserIdentityEnum.forName(userDO.getIdentity()));
        return userLoginDTO;
    }

    /**
     * 注册用户的信息校验操作
     * @param param
     */
    private void checkRegisterInfo(UserRegisterParam param) {
        if (param == null) {
            throw new ServiceException(ServiceErrorCodeConstants.REGISTER_INFO_IS_EMPTY);
        }

        // 检查邮箱的格式是否错误  校验邮箱格式 xxx@xxx.xxx
        if (!RegexUtil.checkMail(param.getMail())) {
            // 说明 邮箱没有符合 验证
            throw new ServiceException(ServiceErrorCodeConstants.MAIL_ERROR);
        }

        // 检查电话号码的格式是否正确
        if(!RegexUtil.checkMobile(param.getPhoneNumber())) {
             // 说明 电话的格式出现错误
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_ERROR);
        }

        // 检查身份信息是否正确
        if(UserIdentityEnum.forName(param.getIdentity()) == null) {
            throw new ServiceException(ServiceErrorCodeConstants.IDENTITY_ERROR);
        }

        // 校验如果身份是管理员的话密码必填
        if(param.getIdentity().equalsIgnoreCase(UserIdentityEnum.ADMIN.name())
            && !StringUtils.hasText(param.getPassword())) {
            // 如果 身份为 管理员并且 所输入的密码为空，那么就返回错误
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_IS_EMPTY);
        }

        // 在密码输入的基础上 校验 密码是否是符合格式
        if(StringUtils.hasText(param.getPassword())
            && !RegexUtil.checkPassword(param.getPassword())) {
            // 如果不符合的话就进行抛出异常
            throw new ServiceException(ServiceErrorCodeConstants.PASSWORD_ERROR);
        }

        // 检验邮箱是否使用过
        if(checkMailUsed(param.getMail())) {
            throw new ServiceException(ServiceErrorCodeConstants.MAIL_USED);
        }

        // 检查手机号是否被使用
        if(StringUtils.hasText(param.getPhoneNumber())
            && checkPhoneNumberUsed(param.getPhoneNumber())) {
            throw new ServiceException(ServiceErrorCodeConstants.PHONE_NUMBER_USED);
        }
    }

    /**
     * 检查手机号是否被使用
     * @param phoneNumber
     * @return
     */
    private boolean checkPhoneNumberUsed(String phoneNumber) {
        // 因为这个是从数据库中进行查询，并且 手机号是加密存储的，从数据库中取出使用也需要进行解密
        // 使用 TypeHandler 这个将其在 存入的时候自动加密，取出自动解密
        // 但是只是 手机号这个 String 类需要进行处理，那么就对其进行封装处理 封装成 Encrypt类
        int count = userMapper.countByPhone(new Encrypt(phoneNumber));
        return count > 0;
    }

    /**
     * 检验邮箱是否使用过
     * @param mail
     * @return
     */
    private boolean checkMailUsed(String mail) {
        int count = userMapper.countByMail(mail);
        return count > 0; // 如果大于 0 那么说明使用过，直接返回 true
    }


}
